package com.xxxx.seckill.controller;

import com.wf.captcha.ArithmeticCaptcha;
import com.xxxx.seckill.VO.GoodsVO;
import com.xxxx.seckill.VO.ResponseBean;
import com.xxxx.seckill.VO.ResponseBeanEnum;
import com.xxxx.seckill.config.AccessLimit;
import com.xxxx.seckill.exception.GlobalException;
import com.xxxx.seckill.pojo.Order;
import com.xxxx.seckill.pojo.SeckillMessage;
import com.xxxx.seckill.pojo.SeckillOrder;
import com.xxxx.seckill.pojo.User;
import com.xxxx.seckill.rabbitmq.MQSender;
import com.xxxx.seckill.service.IGoodsService;
import com.xxxx.seckill.service.IOrderService;
import com.xxxx.seckill.service.ISeckillOrderService;
import com.xxxx.seckill.utils.JsonUtil;
//import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 秒杀
 */
@Slf4j
@Controller
@RequestMapping("/seckill")
public class SeckillController implements InitializingBean { // implements InitializingBean：从第52节课开始

    @Autowired
    private IGoodsService goodsService;

    @Autowired
    private ISeckillOrderService seckillOrderService;

    @Autowired
    private IOrderService orderService;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private MQSender mqSender;

    @Autowired
    private RedisScript<Long> redisScript;

    private Map<Long, Boolean> emptyStockMap = new HashMap<>();

    /**
     * 功能描述：秒杀
     *
     * @param model   :
     * @param user    :
     * @param goodsId :
     * @return :
     */
/*    @RequestMapping("/doSeckill")
    public String doSeckill(Model model, User user, Long goodsId) {
        if (null == user) {
            return "login";
//            return "redirect:/login";
        }
        model.addAttribute("user", user);
        GoodsVO goods = goodsService.findGoodsVOByGoodsId(goodsId);
        // 判断库存
        if (goods.getSeckillStock() <= 0) {
            model.addAttribute("errorMessage", ResponseBeanEnum.EMPTY_STOCK.getMessage());
            return "seckillFailed";
        }
        // 判断是否重复抢购
        SeckillOrder seckillOrder = seckillOrderService.getOne(new QueryWrapper<SeckillOrder>()
                .eq("user_id", user.getId())
                .eq("goods_id", goodsId)
        );
        if (seckillOrder != null) {
            model.addAttribute("errorMessage", ResponseBeanEnum.DUPLICATE_ORDER.getMessage());
            return "seckillFailed";
        }
        // 抢购下单
        Order order = orderService.seckill(user, goods);
        model.addAttribute("order", order);
        model.addAttribute("goods", goods);
        return "orderDetail";
    }*/

    /**
     * 功能描述：秒杀（截至第51节课的旧写法）
     *
     * @param user    :
     * @param goodsId :
     * @return :
     */
    @RequestMapping(value = "/doSeckill51", method = RequestMethod.POST)
    @ResponseBody // 返回类型是 ResponseBean，所以必须要加上这个注解
    public ResponseBean doSeckill51(User user, Long goodsId) {
        if (null == user) {
            return ResponseBean.error(ResponseBeanEnum.USER_NOT_EXISTS);
        }
        // 初步判断是否重复抢购，在service层还要再判断一次
        GoodsVO goods = goodsService.findGoodsVOByGoodsId(goodsId);
        if (goods.getSeckillStock() <= 0) {
            // model.addAttribute("errorMessage", ResponseBeanEnum.EMPTY_STOCK.getMessage());
            return ResponseBean.error(ResponseBeanEnum.EMPTY_STOCK);
        }
/*        SeckillOrder seckillOrder = seckillOrderService.getOne(
                new QueryWrapper<SeckillOrder>()
                        .eq("user_id", user.getId())
                        .eq("goods_id", goodsId)
        );*/
        SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("seckill_order:" + user.getId() + ":" + goods.getId());
        if (seckillOrder != null) {
            // model.addAttribute("errorMessage", ResponseBeanEnum.DUPLICATE_ORDER.getMessage());
            return ResponseBean.error(ResponseBeanEnum.DUPLICATE_ORDER);
        }

        // 抢购下单
        Order order = orderService.seckill(user, goods);
        return ResponseBean.success(order);
    }

    /**
     * 功能描述：秒杀
     *
     * @param user    :
     * @param goodsId :
     * @return :
     */
    @RequestMapping(value = "/{fragment}/doSeckill", method = RequestMethod.POST)
    @ResponseBody // 返回类型是 ResponseBean，所以必须要加上这个注解
    public ResponseBean doSeckill(@PathVariable String fragment, User user, Long goodsId) {
        if (null == user) {
            return ResponseBean.error(ResponseBeanEnum.USER_NOT_EXISTS);
        }
        ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
        boolean check = orderService.checkFragment(user, goodsId, fragment);
        if (!check) {
            return ResponseBean.error(ResponseBeanEnum.REQUEST_ILLEGAL);
        }
        // 判断是否重复抢购
        SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("seckill_order:" + user.getId() + ":" + goodsId);
        if (seckillOrder != null) {
            return ResponseBean.error(ResponseBeanEnum.DUPLICATE_ORDER);
        }
        // 内存标记，减少对Redis的访问
        if (emptyStockMap.get(goodsId)) {
            return ResponseBean.error(ResponseBeanEnum.EMPTY_STOCK);
        }
        // 预减库存操作
        Long seckillStock = valueOperations.decrement("seckill_goods_stock:" + goodsId); // 扣除具有原子性，返回值是递减之后的库存
//        if (null == seckillStock) {
//            return ResponseBean.error(ResponseBeanEnum.ERROR);
//        }
//        Long seckillStock = redisTemplate.execute(redisScript, Collections.singletonList("seckill_goods_stock:" + goodsId), Collections.EMPTY_LIST);
        if (seckillStock < 0) {
//        if (seckillStock <= 0) {
            emptyStockMap.put(goodsId, true);
//            valueOperations.increment("seckill_goods_stock:" + goodsId); // 库存恢复到0
            return ResponseBean.error(ResponseBeanEnum.EMPTY_STOCK);
        }
        // 下单
        SeckillMessage seckillMessage = new SeckillMessage(user, goodsId);
        mqSender.sendSeckillMessage(JsonUtil.object2JsonStr(seckillMessage)); // 异步操作，流量削峰
        return ResponseBean.success(0); // 0表示正在排队中
    }

    /**
     * 功能描述：获取秒杀结果
     *
     * @param user:
     * @param goodsId:
     * @return : orderId:成功，-1:秒杀失败，0:排队中
     */
    @RequestMapping(value = "/result", method = RequestMethod.GET)
    @ResponseBody
    public ResponseBean getResult(User user, Long goodsId) {
        if (null == user) {
            return ResponseBean.error(ResponseBeanEnum.USER_NOT_EXISTS);
        }
        Long orderId = seckillOrderService.getResult(user, goodsId);
        return ResponseBean.success(orderId);
    }

    @RequestMapping(value = "/captcha", method = RequestMethod.GET)
    public void captcha(User user, Long goodsId, HttpServletResponse response) {
        if (null == user || null == goodsId || goodsId <= 0) {
            throw new GlobalException(ResponseBeanEnum.REQUEST_ILLEGAL);
        }
        // 设置请求头为输出图片的类型
        response.setContentType("image/jpg");
        response.setHeader("Pragma", "No-cache"); // 输错了要刷新验证码，防止缓存带来错误
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0); // 永不失效
        // 生成验证码，结果就不放在 session 里了，而是将结果放入 Redis
        ArithmeticCaptcha arithmeticCaptcha = new ArithmeticCaptcha(130, 32, 3);
        redisTemplate.opsForValue().set("seckill_captcha:" + user.getId() + ":" + goodsId, arithmeticCaptcha.text(), 300, TimeUnit.SECONDS);
        try {
            arithmeticCaptcha.out(response.getOutputStream());
        } catch (IOException e) {
//            log.error("验证码生成失败", e.getMessage());
            log.error("验证码生成失败", e);
        }
    }

    /**
     * 功能描述：获取秒杀接口
     */
    @AccessLimit(second = 5, maxCount = 5, needLogin = true)
    @RequestMapping(value = "/path", method = RequestMethod.GET)
    @ResponseBody
    public ResponseBean getSeckillPath(User user, Long goodsId, String captcha, HttpServletRequest request) {
        if (null == user) {
            return ResponseBean.error(ResponseBeanEnum.USER_NOT_EXISTS);
        }
        boolean check = orderService.checkCaptcha(user, goodsId, captcha);
        if (!check) {
            return ResponseBean.error(ResponseBeanEnum.CAPTCHA_ERROR);
        }
        String seckillPath = orderService.createSeckillPathFragment(user, goodsId);
        return ResponseBean.success(seckillPath);
    }

    /**
     * 系统初始化，把商品库存数量加载到Redis
     * 从第52节课开始
     *
     * @throws Exception ：
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        List<GoodsVO> list = goodsService.findGoodsVO();
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        list.forEach(goodsVO -> {
            redisTemplate.opsForValue().set("seckill_goods_stock:" + goodsVO.getId(), goodsVO.getSeckillStock());
            emptyStockMap.put(goodsVO.getId(), false);
        });
    }
}
